package io.github.iogogogo.commons.http;

import io.github.iogogogo.commons.exception.BizException;
import io.github.iogogogo.commons.util.JsonParse;
import io.vavr.Tuple;
import io.vavr.Tuple3;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections4.MapUtils;
import org.springframework.lang.NonNull;

import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
 * Created by tao.zeng on 2024/11/19.
 */
@Slf4j
public class OkHttpUtils {

    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private static final MediaType XML = MediaType.parse("application/xml; charset=utf-8");

    private enum SingletonEnum {
        INSTANCE;

        private final OkHttpUtils singletonPattern;

        SingletonEnum() {
            singletonPattern = new OkHttpUtils();
        }

        public OkHttpUtils getInstance() {
            return singletonPattern;
        }
    }

    public static OkHttpUtils getInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }

    private OkHttpClient okHttpClient;

    public OkHttpUtils setOkHttpClient(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
        return this;
    }


    public Tuple3<Integer, Boolean, String> get(@NonNull String url) {
        return get(url, null, null);
    }

    public Tuple3<Integer, Boolean, String> get(@NonNull String url, Map<String, Object> params) {
        return get(url, params, null);
    }


    /**
     * 构建http get请求
     */
    public Tuple3<Integer, Boolean, String> get(@NonNull String url, Map<String, Object> params, Map<String, String> headers) {
        Request request = processGetParameter(url, params, headers);
        return execute(request);
    }


    public Tuple3<Integer, Boolean, String> postForm(@NonNull String url) {
        return postForm(url, null, null);
    }


    public Tuple3<Integer, Boolean, String> postForm(@NonNull String url, Map<String, Object> params) {
        return postForm(url, params, null);
    }

    /**
     * 构建http post from 请求
     */
    public Tuple3<Integer, Boolean, String> postForm(@NonNull String url, Map<String, Object> params, Map<String, String> headers) {
        Request request = processPostParameter(url, params, headers);
        return execute(request);
    }


    public Tuple3<Integer, Boolean, String> postBody(@NonNull String url) {
        return postBody(url, null, null);
    }


    public <T> Tuple3<Integer, Boolean, String> postBody(@NonNull String url, T data) {
        return postBody(url, data, null);
    }

    public Tuple3<Integer, Boolean, String> postBody(@NonNull String url, String dataJson) {
        return postBody(url, dataJson, null);
    }

    /**
     * 构建http post body 请求
     */
    public <T> Tuple3<Integer, Boolean, String> postBody(@NonNull String url, T data, Map<String, String> headers) {
        return postBody(url, JsonParse.tryToJson(data), headers);
    }

    public Tuple3<Integer, Boolean, String> postBody(@NonNull String url, String dataJson, Map<String, String> headers) {
        Request request = processPostBodyParameter(url, dataJson, headers);
        return execute(request);
    }

    private Tuple3<Integer, Boolean, String> execute(Request request) {
        if (Objects.isNull(okHttpClient))
            throw new NullPointerException("okhttp client not configure. Usage: OkHttpUtils.getInstance().setOkHttpClient();");

        try (Response response = okHttpClient.newCall(request).execute()) {
            ResponseBody body = response.body();
            if (Objects.nonNull(body)) {
                return Tuple.of(response.code(), response.isSuccessful(), body.string());
            } else {
                throw new BizException(String.format("%s,%s,%s", "http request execute failure.", request.url(), response.code()));
            }
        } catch (IOException e) {
            log.error("{}", request.url(), e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 构建Post Body 请求参数
     */
    private Request processPostBodyParameter(String url, String dataJson, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder().url(url);

        RequestBody requestBody = RequestBody.create(Objects.nonNull(dataJson) ? dataJson : "{}", JSON);

        if (Objects.nonNull(headers)) builder.headers(Headers.of(headers));

        return builder.post(requestBody).build();
    }


    /**
     * 构建Post From 请求参数
     */
    private Request processPostParameter(String url, Map<String, Object> params, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder().url(url);

        if (Objects.nonNull(headers)) builder.headers(Headers.of(headers));

        if (MapUtils.isNotEmpty(params)) {
            FormBody.Builder formBuilder = new FormBody.Builder();
            params.forEach((k, v) -> formBuilder.add(k, String.valueOf(v)));
            builder.post(formBuilder.build());
        }
        return builder.build();
    }


    /**
     * 构建Get请求参数
     */
    private Request processGetParameter(String url, Map<String, Object> params, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder();
        StringBuilder sb = new StringBuilder(url);
        if (Objects.nonNull(headers)) builder.headers(Headers.of(headers));
        if (MapUtils.isNotEmpty(params)) {
            boolean firstFlag = true;
            for (String key : params.keySet()) {
                if (firstFlag) {
                    sb.append("?").append(key).append("=").append(params.get(key));
                    firstFlag = false;
                } else {
                    sb.append("&").append(key).append("=").append(params.get(key));
                }
            }
        }
        return builder.url(sb.toString()).build();
    }
}
